package com.kasuroid.core;

/**
 * Timer definition.
 * 
 * @author Tomasz Rusak
 *
 */
public class Timer
{
    /** 
     * Number of milliseconds in second. Used for the seconds
     * calculations.
     */
    private static final float mTimerFrameLength = 1000.0f;

    /** Carries delta time in seconds since the last frame. */
    private float mDeltaSecs;

    /** Carries delta time in milliseconds since the last frame. */
    private long mDelta;

    /** Keeps the last frame value. */
    private long mLastFrame;

    /** Keeps the current frame value. */
    private long mCurrentFrame;

    /** Time elapsed since timer started (in milliseconds). */
    private long mTimeElapsed;

    /** true if timer is paused, otherwise false. */
    private boolean mPaused;

    /** true if timer is started, otherwise false. */
    private boolean mStarted;

    /** Unique id of the timer. */
    private int mId;

    /** Keeps the next id of the timer. Timer's ids generator. */
    private static int mNextId = 0;

    /**
     * Constructs the timer.
     */
    public Timer()
    {
        mNextId++;
        mId = mNextId;
        mPaused = false;
        mStarted = false;
        mCurrentFrame = mLastFrame = 0;
        mDeltaSecs = 0.0f;
        mTimeElapsed = 0;

        Debug.inf(getClass().getName(), "Timer with id: " + mId + " created!");
    }

    /**
     * Starts the timer.
     * 
     * @return SUCCESS if timer started.
     */
    public int start()
    {
        if (isStarted() == false)
        {
            mCurrentFrame = mLastFrame = System.currentTimeMillis();
            mTimeElapsed = 0;
            mDelta = 0;
            mDeltaSecs = 0.0f;
            mStarted = true;
            mPaused = false;

            Debug.inf(getClass().getName(), "Timer with id: " + mId + " started.");
            return RetCode.SUCCESS;
        }
        else
        {
            Debug.warn(getClass().getName(), "Timer already started!");
            return RetCode.SUCCESS;
        }
    }

    /**
     * Stops the timer.
     * 
     * @return SUCCESS if timer stopped.
     */
    public int stop()
    {
        mLastFrame = mCurrentFrame = 0;
        mDelta = 0;
        mDeltaSecs = 0;
        mTimeElapsed = 0;
        mPaused = false;
        mStarted = false;

        Debug.inf(getClass().getName(), "Timer stopped.");
        return RetCode.SUCCESS;
    }

    /**
     * Pauses the timer.
     * @return SUCCESS if timer paused, otherwise FAILURE.
     */
    public int pause()
    {
        if ((isPaused() == false) &&
            (isStarted() == true))
        {
            mPaused = true;
            mDelta = 0;
            mDeltaSecs = 0.0f;

            Debug.inf(getClass().getName(), "Timer paused.");
            return RetCode.SUCCESS;
        }
        else if (isPaused() == true)
        {
            Debug.warn(getClass().getName(), "Timer already paused");
            return RetCode.SUCCESS;
        }
        else
        {
            Debug.err(getClass().getName(), "Timer must be started!");
            return RetCode.FAILURE;
        }
    }

    /**
     * Resumes the timer.
     * 
     * @return SUCCESS if ok, otherwise FAILURE.
     */
    public int resume()
    {
        if ((isPaused() == true) &&
            (isStarted() == true))
        {
            mCurrentFrame = System.currentTimeMillis();
            mPaused = false;

            Debug.inf(getClass().getName(), "Timer resumed.");
            return RetCode.SUCCESS;
        }
        else if (isStarted() == true)
        {
            Debug.warn(getClass().getName(), "Timer alredy running");
            return RetCode.SUCCESS;
        }
        else
        {
            Debug.err(getClass().getName(), "Can't resume not paused timer!");
            return RetCode.FAILURE;
        }
    }

    /**
     * Updates the timer values. It calculates (yes, in every frame) the time
     * elapsed since last frame in seconds. It is done here, because many
     * objects may need such value. Doing that calculation in for example "getDeltaSecs"
     * would require more CPU.
     * 
     * @return SUCCESS
     */
    public int update()
    {
        if ((isStarted() == true) &&
            (isPaused() == false))
        {
            mLastFrame = mCurrentFrame;
            mCurrentFrame = System.currentTimeMillis();
            mDelta = mCurrentFrame - mLastFrame;
            // Make sure delta is positive.
            if (mDelta < 0)
                mDelta = 0;
            mTimeElapsed += mDelta;
            mDeltaSecs = (float)mDelta / mTimerFrameLength;
        }

        return RetCode.SUCCESS;
    }

    /**
     * Gets the timer start state.
     * 
     * @return true if timer is started, otherwise false.
     */
    public boolean isStarted()
    {
        return mStarted;
    }

    /**
     * Gets the timer pause state.
     * 
     * @return true if timer is paused, otherwise false.
     */
    public boolean isPaused()
    {
        return mPaused;
    }

    /**
     * Gets the timer id.
     * 
     * @return Timer id.
     */
    public int getId()
    {
        return mId;
    }

    /**
     * Returns elapsed time since timer started in millisecs.
     * @return Elapsed time in millisecs.
     */
    public long getTimeElapsedMillis()
    {
        return mTimeElapsed;
    }

    /**
     * Returns elapsed time in seconds since timer started.
     * @return
     */
    public float getTimeElapsedSecs()
    {
        return ((float)mTimeElapsed / mTimerFrameLength);
    }

    public float getTimeDeltaSecs()
    {
        return mDeltaSecs;
    }

    public long getTimeDeltaMillis()
    {
        return mDelta;
    }
}
